package org.global.utils;

import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

public class BeansUtils {
    /**
     * 覆盖策略： 1.如果字段类型不是FieldUtils.isBaseType范围内的类型或不是枚举，则会继续搜索该字段里边的字段
     *          2.如果值不相等，s会覆盖store的值。
     * @param s     源数据
     * @param store 被覆盖的数据
     */
    public static void copy(Object s, Object store, String...exclude) throws IllegalAccessException {
        if (s == null || store == null) return;
        Class<?> sClass = s.getClass(), storeClass = store.getClass();
        if (sClass != storeClass) {
            List<Field> sFields = allField(sClass);

            Map<String, Field> storeFieldMap = allField(storeClass)
                    .stream()
                    .collect(Collectors.toMap(
                            Field::getName,
                            ((field) -> field)
                    ));
            for (Field sField : sFields) {
                String fieldName = sField.getName();
                boolean flag = true;
                for (String string : exclude) {
                    flag &= !string.equals(sField.getName());
                }
                if (!flag) continue;
                if (storeFieldMap.containsKey(fieldName)) {
                    sField.setAccessible(true);
                    Field storeField = storeFieldMap.get(fieldName);
                    storeField.setAccessible(true);
                    if ((sField.isEnumConstant() && storeField.isEnumConstant())
                            || (sField.getType().isEnum() && storeField.getType().isEnum())
                            || (sField.getType().isArray() && storeField.getType().isArray())
                            || (sField.getType().isMemberClass() && storeField.getType().isMemberClass())
                            || (sField.getType().isInterface() && storeField.getType().isInterface())
                            || storeField.isAnnotationPresent(Copy.class)
                            || (FieldUtils.isBaseType(sField.getType().getName()) && FieldUtils.isBaseType(storeField.getType().getName()))) {
                        if (!Objects.equals(sField.get(s), storeField.get(store))) {
                            storeField.set(store, sField.get(s));
                        }
                    } else {
                        copy(sField.get(s), storeField.get(store));
                    }
                }
            }
        } else {
            ReflectionUtils.doWithFields(sClass, field -> {
                field.setAccessible(true);
                Object tf = field.get(store), sf = field.get(s);
                boolean flag = true;
                for (String string : exclude) {
                    flag &= !string.equals(field.getName());
                }
                if (flag) {
                    if (field.isEnumConstant()
                            || field.getType().isArray()
                            || FieldUtils.isBaseType(field.getType().getName())
                            || field.getType().isMemberClass()
                            || field.getType().isInterface()
                            || field.getType().isEnum()) {
                        if (!Objects.equals(sf, tf)) {
                            field.set(store, sf);
                        }
                    } else {
                        copy(sf, tf);
                    }
                }
            });
        }
    }

    private static List<Field> allField(Class<?> cla) {
        List<Field> fields = new ArrayList<>(40);
        while (cla != null) {
            fields.addAll(Arrays.asList(cla.getDeclaredFields()));
            cla = cla.getSuperclass();
        }
        return fields;
    }
}
